#include "pong.h"

int hdr_pong::offset_;
static class PongHeaderClass : public PacketHeaderClass {
public:
	PongHeaderClass() : PacketHeaderClass("PacketHeader/Pong", 
					      sizeof(hdr_pong)) {
		bind_offset(&hdr_pong::offset_);
	}
} class_ponghdr;


static class PongClass : public TclClass {
public:
	PongClass() : TclClass("Agent/Pong") {}
	TclObject* create(int, const char*const*) {
		return (new PongAgent());
	}
} class_pong;


PongAgent::PongAgent() : Agent(PT_PONG), seq(0), oneway(0)
{
	bind("packetSize_", &size_);
}

int PongAgent::command(int argc, const char*const* argv)
{
  if (argc == 2) {
    if (strcmp(argv[1], "send") == 0) {
      // Create a new packet
      Packet* pkt = allocpkt();
      // Access the Pong header for the new packet:
      hdr_pong* hdr = hdr_pong::access(pkt);
      // Set the 'ret' field to 0, so the receiving node
      // knows that it has to generate an echo packet
      hdr->ret = 0;
      hdr->seq = seq++;
      // Store the current time in the 'send_time' field
      hdr->send_time = Scheduler::instance().clock();
      // Send the packet
      send(pkt, 0);
      // return TCL_OK, so the calling function knows that
      // the command has been processed
      return (TCL_OK);
    
    }
    
    else if (strcmp(argv[1], "start-WL-brdcast") == 0) {
      Packet* pkt = allocpkt();
      
      hdr_ip* iph = HDR_IP(pkt);
      hdr_pong* ph = hdr_pong::access(pkt);
      
      iph->daddr() = IP_BROADCAST;
      iph->dport() = iph->sport();
      ph->ret = 0;
      send(pkt, (Handler*) 0);
      return (TCL_OK);
    }

    else if (strcmp(argv[1], "oneway") == 0) {
      oneway=1;
      return (TCL_OK);
    }
  }
  
  // If the command hasn't been processed by PongAgent()::command,
  // call the command() function for the base class
  return (Agent::command(argc, argv));
}


void PongAgent::recv(Packet* pkt, Handler*)
{
  // Access the IP header for the received packet:
  hdr_ip* hdrip = hdr_ip::access(pkt);
  
  // Access the Pong header for the received packet:
  hdr_pong* hdr = hdr_pong::access(pkt);
  

  // check if in brdcast mode
  if ((u_int32_t)hdrip->daddr() == IP_BROADCAST) {
    if (hdr->ret < 2) {
      
      printf("Recv BRDCAST Pong REQ : at %d.%d from %d.%d\n", here_.addr_, here_.port_, hdrip->saddr(), hdrip->sport());
      
      // create reply
      Packet* pktret = allocpkt();

      hdr_pong* hdrret = hdr_pong::access(pktret);
      hdr_ip* ipret = hdr_ip::access(pktret);
      
      hdrret->ret = hdr->ret+1;
      
      Packet::free(pkt);
      // add brdcast address
      ipret->daddr() = IP_BROADCAST;
      ipret->dport() = ipret->sport();

      send(pktret, 0);
    
    } else {
      printf("Recv BRDCAST Pong REPLY : at %d.%d from %d.%d\n", here_.addr_, here_.port_, hdrip->saddr(), hdrip->sport());
      Packet::free(pkt);
    }
    return;
  }
  // Is the 'ret' field < 2 (i.e. the receiving node is being ponged)?
  if (hdr->ret < 2) {
    // Send an 'echo'. First save the old packet's send_time
    double stime = hdr->send_time;
    int rcv_seq = hdr->seq;
    // Create a new packet
    Packet* pktret = allocpkt();
    // Access the Pong header for the new packet:
    hdr_pong* hdrret = hdr_pong::access(pktret);
    // Set the 'ret' field to 1, so the receiver won't send
    // another echo
    hdrret->ret = hdr->ret+1;
    // Discard the packet
    Packet::free(pkt);
    // Set the send_time field to the correct value
    hdrret->send_time = stime;
    // Added by Andrei Gurtov for one-way delay measurement.
    hdrret->rcv_time = Scheduler::instance().clock();
    hdrret->seq = rcv_seq;
    // Send the packet
    send(pktret, 0);
  } else {
    // A packet was received. Use tcl.eval to call the Tcl
    // interpreter with the pong results.
    // Note: In the Tcl code, a procedure
    // 'Agent/Pong recv {from rtt}' has to be defined which
    // allows the user to react to the pong result.
    char out[100];
    // Prepare the output to the Tcl interpreter. Calculate the
    // round trip time
    if (oneway) //AG
      	sprintf(out, "%s recv %d %d %3.1f %3.1f", name(), 
	    hdrip->src_.addr_ >> Address::instance().NodeShift_[1],
	    hdr->seq, (hdr->rcv_time - hdr->send_time) * 1000,
	    (Scheduler::instance().clock()-hdr->rcv_time) * 1000);
    else sprintf(out, "%s recv %d %3.1f", name(), 
	    hdrip->src_.addr_ >> Address::instance().NodeShift_[1],
	    (Scheduler::instance().clock()-hdr->send_time) * 1000);
    Tcl& tcl = Tcl::instance();
    tcl.eval(out);
    // Discard the packet
    Packet::free(pkt);
  }
}


